
#include "stdafx.h"
#include "UserTimer.h"
#include "Utility.h"
#ifndef _USE_TIMER_OUTSIDE_
#include "UserMessage.h"
#include "LogClass.h"
#include "VirtualWin.h"
#endif

CUserTimer::CUserTimer()
{
}

CUserTimer::~CUserTimer()
{
}

void CUserTimer::DestroyTimer()
{
	lock_Timer.Lock();
	for (intptr_t i = m_Timer.GetSize() - 1; i >= 0; i--)
	{
		CTimerParam *p = m_Timer.GetAt(i); if (!p) continue;
		p->Stop(); while (CheckThread(p->hThread, p->threadRunning)) Sleep(10);
		delete p;
	}
	lock_Timer.Unlock();
	m_Timer.RemoveAll();
}

#ifndef _USE_TIMER_OUTSIDE_
HANDLE CUserTimer::SetTimer(CVirtualDlg *caller, UINT_PTR nIDEvent, uintptr_t nElapse, bool runonce)
{
	return SetTimer(new CTimerParam(caller, nullptr, nIDEvent, nElapse, runonce));
}
HANDLE CUserTimer::SetLogTimer(CVirtualDlg *caller, UINT_PTR nIDEvent, uintptr_t nElapse, bool runonce)
{
	CTimerParam *pParam = new CTimerParam(caller, nullptr, nIDEvent, nElapse, runonce);
	pParam->logDispatch = true; return SetTimer(pParam);
}
HANDLE CUserTimer::SetLogTimer(HWND caller, UINT_PTR nIDEvent, uintptr_t nElapse, bool runonce)
{
	CTimerParam *pParam = new CTimerParam(nullptr, caller, nIDEvent, nElapse, runonce);
	pParam->logDispatch = true; return SetTimer(pParam);
}
#endif
HANDLE CUserTimer::SetTimer(HWND caller, UINT_PTR nIDEvent, uintptr_t nElapse, bool runonce)
{
	return SetTimer(new CTimerParam(
#ifndef _USE_TIMER_OUTSIDE_
		nullptr,
#endif
		caller, nIDEvent, nElapse, runonce));
}
HANDLE CUserTimer::SetTimer(CTimerParam *pParam)
{
	HANDLE result = pParam ? pParam->createThread() : nullptr;
	lock_Timer.Lock();
	for (intptr_t i = m_Timer.GetSize() - 1; i >= 0; i--)
	{
		CTimerParam *p = m_Timer.GetAt(i);
		if (p)
		{
			if (CheckThread(p->hThread, p->threadRunning)) continue;
			delete p;
		}
		m_Timer.RemoveAt(i);
	}
	if (pParam) m_Timer.Add(pParam);
	lock_Timer.Unlock();
	return result;
}
void CUserTimer::KillTimer(HANDLE hThread)
{
	lock_Timer.Lock();
	for (intptr_t i = m_Timer.GetSize() - 1; i >= 0; i--)
	{
		CTimerParam *p = m_Timer.GetAt(i);
		if (p)
		{
			if (p->hThread == hThread) p->Stop();
			if (CheckThread(p->hThread, p->threadRunning)) continue;
			delete p;
		}
		m_Timer.RemoveAt(i);
	}
	lock_Timer.Unlock();
}


CTimerParam::CTimerParam(
#ifndef _USE_TIMER_OUTSIDE_
	CVirtualDlg *caller,
#endif
	HWND hcaller, UINT_PTR nidevent, uintptr_t nelapse, bool runonce) :
#ifndef _USE_TIMER_OUTSIDE_
	Caller(caller),
#endif
	hCaller(hcaller)
{
	nIDEvent = nidevent; nElapse = nelapse; runOnce = runonce;
	hThread = nullptr; stopThread = threadRunning = false;
	hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
#ifndef _USE_TIMER_OUTSIDE_
	logDispatch = false;
#endif
}
CTimerParam::~CTimerParam()
{
	CloseHandle(hEvent);
}
DWORD WINAPI StartThread_Timer(LPVOID p)
{
	CTimerParam *pParam = (CTimerParam *)p; if (!pParam) return -1;
	pParam->timerThread();
	pParam->threadRunning = false;
	return 0;
}
HANDLE CTimerParam::createThread()
{
	threadRunning = true; hThread = CreateThread(NULL, 0, StartThread_Timer, this, 0, NULL);
	return hThread;
}

void CTimerParam::timerThread()
{
	for (stopThread = false; !stopThread;)
	{
		WaitForSingleObject(hEvent, (DWORD)nElapse);
		if (!stopThread)
		{
#ifndef _USE_TIMER_OUTSIDE_
#ifdef _WIN64
			if (logDispatch) gLog.add(L"%Id Dispatch WM_USER_TIMER", ::GetTickCount64());
#else
			if (logDispatch) gLog.add(L"%d Dispatch WM_USER_TIMER", ::GetTickCount());
#endif
			if (Caller) Caller->ProcUserTimer(nIDEvent); else PostMsg(hCaller, WM_USER_TIMER, nIDEvent);
#else
			::PostMessage(hCaller, WM_USER_TIMER, nIDEvent, 0);
#endif
			if (runOnce) stopThread = true;
		}
	}
}
